#include "mainwindow.hpp"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    this->sevenSegCount = 0;

    //Create Main Toolbar
    ui->mainToolBar->addAction(ui->actionOpen);
    ui->mainToolBar->addSeparator();
    ui->mainToolBar->addAction(ui->actionTrace);
    ui->mainToolBar->addAction(ui->actionStart);
    ui->mainToolBar->addAction(ui->actionPause);
    ui->mainToolBar->addAction(ui->actionStop);
    ui->mainToolBar->addSeparator();
    ui->mainToolBar->addAction(ui->actionSevenSegment);

    pix = new Pix(this);

    //Get the Settings and initialize MainWindow
    appSettings = AppSettings::getInstance();
    this->restoreGeometry(appSettings->getMainWindowGeometry());
    this->restoreState(appSettings->getMainWindowState());

    //Set up the DockWidgetAreas
    this->setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea);
    this->setCorner(Qt::TopRightCorner, Qt::RightDockWidgetArea);
    this->setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);

    //Generate the DockWidgets
    codeView = new CodeView(this);
    codeView->setObjectName("CodeView");
    /*if (appSettings->contains("general/mainWindowState"))
	this->restoreDockWidget(codeView);*/
    this->setCentralWidget(codeView);

    breakpointView = new Breakpoints(this);
    breakpointView->setObjectName("Breakpoints");
    if (appSettings->contains("general/mainWindowState"))
	this->restoreDockWidget(breakpointView);
    else
	this->addDockWidget(Qt::RightDockWidgetArea, breakpointView);
    ui->menuWindows->addAction(breakpointView->toggleViewAction());

    papView = new PapView(pix, this);
    papView->setObjectName("PAP");
    if (appSettings->contains("general/mainWindowState"))
	this->restoreDockWidget(papView);
    else
	this->addDockWidget(Qt::LeftDockWidgetArea, papView);
    ui->menuWindows->addAction(papView->toggleViewAction());

    registerView = new RegisterView(this);
    registerView->setObjectName("Registers");
    if (appSettings->contains("general/mainWindowState"))
	this->restoreDockWidget(registerView);
    else
	this->addDockWidget(Qt::BottomDockWidgetArea, registerView);
    ui->menuWindows->addAction(registerView->toggleViewAction());

    stackView = new StackView(this);
    stackView->setObjectName("Stack");
    if (appSettings->contains("general/mainWindowState"))
	this->restoreDockWidget(stackView);
    else
	this->addDockWidget(Qt::RightDockWidgetArea, stackView);
    ui->menuWindows->addAction(stackView->toggleViewAction());

    watchList = new WatchList(this);
    watchList->setObjectName("WatchList");
    if (appSettings->contains("general/mainWindowState"))
	this->restoreDockWidget(watchList);
    else
	this->addDockWidget(Qt::RightDockWidgetArea, watchList);
    ui->menuWindows->addAction(watchList->toggleViewAction());

    hwInterfaceView = new HardwareInterfaceView(this);
    hwInterfaceView->setObjectName("HardwareInterface");
    if (appSettings->contains("general/mainWindowState"))
	this->restoreDockWidget(hwInterfaceView);
    else
	this->addDockWidget(Qt::RightDockWidgetArea, hwInterfaceView);
    ui->menuWindows->addAction(hwInterfaceView->toggleViewAction());

    this->msgBoxWatchdogTimeout = new QMessageBox(QMessageBox::Critical, "Watchdog Timeout", "Watchdog Timeout", QMessageBox::Ok, this);

    // Connect
    // Signals from GUI
    connect(registerView, SIGNAL(requestChangeRegister(int,int)), this, SLOT(onRequestChangeRegister(int,int)));
    connect(registerView, SIGNAL(requestChangeBank(bool)), this, SIGNAL(requestChangeBank(bool)));
    connect(watchList, SIGNAL(variableTooltipChanged(QString,QString)), codeView, SLOT(onCustomTooltip(QString,QString)));
    connect(breakpointView, SIGNAL(jumpToBreakpoint(QString)), codeView, SLOT(onJumpToBreakpoint(QString)));
    connect(hwInterfaceView, SIGNAL(changeHardwareInterfaceState(QString,bool)), pix, SLOT(onChangeHardwareInterfaceState(QString,bool)));
    connect(hwInterfaceView, SIGNAL(manualReceive(QString)), this, SLOT(onManualReceive(QString)));
    connect(hwInterfaceView, SIGNAL(manualSend()), this, SLOT(onManualSend()));

    connect(pix, SIGNAL(codeFileParsed()), this, SLOT(onCodeFileParsed()));

    connect(pix, SIGNAL(switchBankSignal(bool)), registerView, SLOT(onBankChanged(bool)));
    connect(pix, SIGNAL(registerChanged(Register*)), registerView, SLOT(onRegisterChanged(Register*)));
    connect(pix, SIGNAL(registerChanged(Register*)), watchList, SLOT(onVariableChanged(Register*)));

    connect(pix, SIGNAL(customHighlightingRule(QString, int)), codeView, SLOT(onCustomHighlightingRule(QString, int)));
    connect(pix, SIGNAL(customTooltip(QString,QString)), codeView, SLOT(onCustomTooltip(QString,QString)));
    connect(pix, SIGNAL(variableFound(QString,QString)), watchList, SLOT(onVariableAdded(QString,QString)));

    connect(pix, SIGNAL(breakPointToggled(int,bool,QString,QString)), codeView, SLOT(onBreakpointToggled(int,bool)));
    connect(pix, SIGNAL(breakPointToggled(int,bool,QString,QString)), breakpointView, SLOT(onBreakpointToggled(int,bool,QString,QString)));
    connect(codeView, SIGNAL(requestBreakpoint(int)), pix, SIGNAL(requestBreakpoint(int)));

    connect(pix, SIGNAL(pushStack(int,int)), stackView, SLOT(onPushStack(int,int)));
    connect(pix, SIGNAL(popStack()), stackView, SLOT(onPopStack()));
    connect(pix, SIGNAL(stackOverflow()), stackView, SLOT(onStackOverflow()));

    connect(pix, SIGNAL(programCounterChanged(int)), registerView, SLOT(onProgramCounterChanged(int)));
    connect(pix, SIGNAL(programCounterChanged(int)), codeView, SLOT(onProgramCounterChanged(int)));
    connect(pix, SIGNAL(programCounterChanged(int)), this, SLOT(updateStatusbar()));
    connect(pix, SIGNAL(programCounterChanged(int)), papView, SLOT(onProgramCounterChanged(int)));
    connect(pix, SIGNAL(simulationStateChanged(bool,int)), codeView, SLOT(onSimulationStateChanged(bool,int)));
    connect(pix, SIGNAL(simulationStateChanged(bool,int)), this, SLOT(onSimulationStateChanged(bool)));

    connect(pix, SIGNAL(hardwareInterfaceError(QString,QString)), hwInterfaceView, SLOT(onError(QString,QString)));
    connect(pix, SIGNAL(hardwareInterfaceStateChanged(bool)), hwInterfaceView, SLOT(onHardwareInterfaceStateChanged(bool)));
    connect(pix, SIGNAL(dataRead(QString)), hwInterfaceView, SLOT(onDataRead(QString)));
    connect(pix, SIGNAL(dataWritten(QString)), hwInterfaceView, SLOT(onDataWritten(QString)));

    connect(this, SIGNAL(reset()), this, SLOT(onReset()));
    connect(this, SIGNAL(reset()), this->pix, SLOT(reset()));

    // Create the Statusbar
    this->runtime = new QLabel(this->pix->getRuntimeString(), this);
    this->watchdog = new QLabel(this->pix->getWatchdogString(), this);
    this->information = new QLabel("", this);
    ui->statusBar->addWidget(this->runtime, 12);
    ui->statusBar->addWidget(this->watchdog, 12);
    ui->statusBar->addWidget(this->information, 100);

    this->registerView->setHighlighting(false);
    emit reset();
}

MainWindow::~MainWindow()
{
    AppSettings::releaseInstance();

    while (!this->sevenSegList.isEmpty())
	delete this->sevenSegList.takeFirst();

    delete ui;
    delete codeView;
    delete breakpointView;
    delete papView;
    delete registerView;
    delete stackView;
    delete watchList;

    if (pix->isRunning())
    {
	QMetaObject::invokeMethod(pix, "onStop", Qt::DirectConnection);
	if(!pix->wait(1000))
	    pix->quit();

	if(!pix->wait(1000))
	{
	    qWarning() << "Quit does not work .. Using Terminate!";
	    pix->terminate();
	}
    }
}

void MainWindow::changeEvent(QEvent *e)
{
    QMainWindow::changeEvent(e);
    switch (e->type()) {
    case QEvent::LanguageChange:
        ui->retranslateUi(this);
        break;
    default:
        break;
    }
}


void MainWindow::closeEvent(QCloseEvent *ce)
{
    appSettings->setMainWindowGeometry(this->saveGeometry());
    appSettings->setMainWindowState(this->saveState());
}

void MainWindow::on_actionOpen_triggered()
{
    QString filepath = QFileDialog::getOpenFileName( this, tr("Open"), QDir::currentPath(), tr("LST-Files (*.lst *.LST)"), 0, QFileDialog::DontUseNativeDialog );

    if (!filepath.isEmpty())
    {
	emit reset();
	emit parseLstFile(filepath);
	/* TODO: Connect all Resets */
    }
}


void MainWindow::onCodeFileParsed()
{
    ProgramManagement *prog = pix->getProgramManagement();
    codeView->addCommands(prog->getCommandList());
    papView->createPap();
    delete prog;

    this->registerView->setHighlighting(true);

    // Activate GUI Controls
    ui->actionStart->setEnabled(true);
    ui->actionTrace->setEnabled(true);
    ui->actionPause->setEnabled(true);
    ui->actionStop->setEnabled(true);
    ui->actionResetTimer->setEnabled(true);
    ui->actionResetWatchdog->setEnabled(true);
}


void MainWindow::onReset()
{
    //Reset to Defaults
    emit stop();
    this->codeView->reset();
    this->stackView->reset();
    this->watchList->reset();
    this->updateStatusbar();
    this->papView->reset();
}


void MainWindow::resetToProgramStart()
{
    //Reset after Watchdog Timeout
    //this->pix->resetToProgramStart();
    this->updateStatusbar();
}


RegisterView *MainWindow::getRegisterView()
{
    return this->registerView;
}


void MainWindow::on_actionNew_triggered()
{
    emit reset();
}


void MainWindow::on_actionClose_triggered()
{
    qApp->quit();
}

void MainWindow::on_actionStart_triggered()
{
    emit start();
}


void MainWindow::on_actionStop_triggered()
{
    QMetaObject::invokeMethod(pix, "onStop", Qt::DirectConnection);
    //this->papView->resetToProgramStart();
    this->resetToProgramStart();
}


void MainWindow::on_actionTrace_triggered()
{
    emit trace();
}


void MainWindow::on_actionPause_triggered()
{
    if (ui->actionPause->isChecked())
	QMetaObject::invokeMethod(pix, "onPause", Qt::DirectConnection);
    else
	emit start();
}


void MainWindow::on_actionSevenSegment_triggered()
{
    SevenSegmentView *newView = new SevenSegmentView(registerView, this);
    connect(this->pix, SIGNAL(registerChanged(Register*)), newView, SLOT(onRegisterChanged(Register*)));
    newView->setObjectName("SevenSegment" + sevenSegCount);
    newView->setAttribute(Qt::WA_DeleteOnClose, true);
    this->sevenSegList.append(newView);
    this->sevenSegCount++;
    this->addDockWidget(Qt::LeftDockWidgetArea, newView);
    ui->menuWindows->addAction(newView->toggleViewAction());
}


void MainWindow::updateStatusbar()
{
    this->runtime->setText(this->pix->getRuntimeString());
    this->watchdog->setText(this->pix->getWatchdogString());
}


void MainWindow::onWatchdogTimeout()
{
    emit stop();
    int exitvalue = this->msgBoxWatchdogTimeout->exec();
    if(exitvalue == QMessageBox::Ok)
    {
	//this->pix->resetToProgramStart();
    }
}


/*void MainWindow::on_actionFastMode_toggled(bool state)
{
    if (state)
    {
	// Disconnet not needed Signals to speed the GUI up
	disconnect(pix, SIGNAL(switchBankSignal(bool)), registerView, SLOT(onBankChanged(bool)));
	disconnect(pix, SIGNAL(stackOverflow()), stackView, SLOT(onStackOverflow()));
	disconnect(breakpointView, SIGNAL(jumpToBreakpoint(QString)), codeView, SLOT(onJumpToBreakpoint(QString)));
	disconnect(registerView, SIGNAL(requestChangeRegister(int,int)), this, SLOT(onRequestChangeRegister(int,int)));
	disconnect(pix, SIGNAL(programCounterChanged(int)), papView, SLOT(onProgramCounterChanged(int)));
	disconnect(pix, SIGNAL(programCounterChanged(int)), papView, SLOT(onProgramCounterChanged(int)));

	this->registerView->setHighlighting(false);
	this->pix->setFastMode(true);
	this->information->setText("Fast Mode activated!");

    } else {
	// Reconnect all Signals to GUI
	connect(pix, SIGNAL(switchBankSignal(bool)), registerView, SLOT(onBankChanged(bool)));
	connect(pix, SIGNAL(stackOverflow()), stackView, SLOT(onStackOverflow()));
	connect(breakpointView, SIGNAL(jumpToBreakpoint(QString)), codeView, SLOT(onJumpToBreakpoint(QString)));
	connect(registerView, SIGNAL(requestChangeRegister(int,int)), this, SLOT(onRequestChangeRegister(int,int)));
	connect(pix, SIGNAL(programCounterChanged(int)), papView, SLOT(onProgramCounterChanged(int)));
	//connect(pix, SIGNAL(programCounterChanged(int)), papView, SLOT(onProgramCounterChanged(int)));

	this->registerView->setHighlighting(true);
	this->pix->setFastMode(false);
	this->information->clear();
    }
}
*/

void MainWindow::onSimulationStateChanged(bool state)
{
    ui->actionStart->setEnabled(true);
    ui->actionTrace->setEnabled(true);
    ui->actionPause->setEnabled(true);
    ui->actionStop->setEnabled(true);

    switch(this->pix->getSimulationMode())
    {
    case Pix::RUN:
	ui->actionPause->setChecked(false);
	ui->actionStart->setDisabled(true);
	ui->actionTrace->setDisabled(true);
	break;

    case Pix::TRACE:
	if (state)
	{
	    ui->actionPause->setChecked(false);
	    ui->actionStart->setDisabled(true);
	    ui->actionStop->setDisabled(true);
	    ui->actionPause->setDisabled(true);
	    ui->actionTrace->setDisabled(true);
	}
	break;

    case Pix::STOP:
	ui->actionPause->setChecked(false);
	ui->actionPause->setDisabled(true);
	ui->actionStop->setDisabled(true);
	break;

    case Pix::PAUSE:
	if (state)
	    ui->actionPause->setChecked(true);
	else
	    ui->actionPause->setChecked(false);
	break;

    case Pix::SLEEP:
    case Pix::BREAK:
    default:
	break;
    }
}


void MainWindow::onRequestChangeRegister(int address, int value)
{
    QMetaObject::invokeMethod(pix, "onRequestChangeRegister", Qt::DirectConnection, Q_ARG(int, address), Q_ARG(int, value));
}


void MainWindow::onManualSend()
{
    QMetaObject::invokeMethod(pix, "hwInterfaceManualSend", Qt::DirectConnection);
}


void MainWindow::onManualReceive(QString data)
{
    QMetaObject::invokeMethod(pix, "hwInterfaceManualReceive", Qt::DirectConnection, Q_ARG(QString, data));
}


void MainWindow::on_actionAbout_triggered()
{
    QMessageBox::about(this, "About", "Created by Lars Briem and Tobias Rausch\nUniversity of Cooperativ Education Karlsruhe\nPIC Simulator");
}


void MainWindow::on_actionHelp_triggered()
{
    // Hack to open Helpfile
    QResource res(":/help/helpfile");
    qDebug() << res.fileName().toAscii().constData();
    //system("cd .. & cd res & Benutzerdokumentation.pdf");
}
